package com.zimin.cloud.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.zimin.cloud.gateway.common.SignUtils;
import com.zimin.shared.enums.ResponseStatusEnum;
import com.zimin.shared.pojo.Result;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 签名过滤器
 *
 * @author zimin
 * @since 2021/12/15 2:21 PM
 */
//@Component
public class SignGlobalFilter implements GlobalFilter {

    private static final String API_PREFIX = "/web/";
    private static final Long TIME_STAMP = 30 * 60 * 1000L;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //根据URL判断接口是否需要校验
        var request = exchange.getRequest();
        var url = request.getURI().getPath();
        if (url.startsWith(API_PREFIX)) {
            //获取请求方法
            TreeMap paramsMap;
            switch (request.getMethod()) {
                case GET:
                case DELETE:
                    paramsMap = new TreeMap(request.getQueryParams());
                    break;
                case POST:
                case PUT:
                    String requestBodyStr = resolveBodyFromRequest(request);
                    paramsMap = JSON.parseObject(requestBodyStr, TreeMap.class);
                    break;
                default:
                    paramsMap = new TreeMap();
                    break;
            }
            // 校验时间戳是不是在范围内
            setTreemap(paramsMap);
            var timestampObj = paramsMap.get("timestamp");
            var response = exchange.getResponse();
            if (ObjectUtils.isEmpty(timestampObj)) {
                //时间戳不可为空
                return responseWrite(response, ResponseStatusEnum.TIMESTAMP_IS_NULL);
            }
            var timestamp = Long.parseLong(String.valueOf(timestampObj));
            var nowTimeStamp = System.currentTimeMillis();
            if (nowTimeStamp - timestamp > TIME_STAMP) {
                //时间戳不在范围内
                return responseWrite(response, ResponseStatusEnum.TIMESTAMP_IS_VALID);
            }
            var saltObj = paramsMap.get("salt");
            if (ObjectUtils.isEmpty(saltObj)) {
                //密钥不可以为空
                return responseWrite(response, ResponseStatusEnum.KEY_IS_NULL);
            }
            var headers = request.getHeaders();
            var signList = headers.get("sign");
            if (ObjectUtils.isEmpty(signList)) {
                //签名是否合法
                return responseWrite(response, ResponseStatusEnum.SIGN_IS_NULL);
            }
            // 校验签名是否合法
            var _sign = signList.get(0);
            var sign = SignUtils.createHashedQueryString(paramsMap, timestamp, String.valueOf(saltObj));
            if (!_sign.equals(sign)) {
                //签名校验失败
                return responseWrite(response, ResponseStatusEnum.SIGN_IS_VALID);
            }
        }
        return chain.filter(exchange);
    }

    private void setTreemap(TreeMap<String, Object> paramsMap) {
        paramsMap.forEach((k, v) -> {
            paramsMap.put(k, ((List) v).get(0));
        });
    }

    /**
     * <p>获取post 参数</p>
     *
     * @param serverHttpRequest
     * @return java.lang.String
     * @since 2021-12-15 17:39:18
     */
    private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
        //获取请求体
        var body = serverHttpRequest.getBody();
        var bodyRef = new AtomicReference<String>();
        body.subscribe(buffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            DataBufferUtils.release(buffer);
            bodyRef.set(charBuffer.toString());
        });
        //获取request body
        return bodyRef.get();
    }

    public Mono<Void> responseWrite(ServerHttpResponse response, ResponseStatusEnum responseStatusEnum) {
        //指定编码，否则在浏览器中会中文乱码
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        response.setStatusCode(HttpStatus.OK);
        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(Result.error(responseStatusEnum)));
        }));
    }

}
